# -*- coding: utf-8 -*-
"""
Python 3.9.13
完整流程：
  1) 从 config.json 读取参数
  2) GET 入口页 -> 解析“会员登入”链接
  3) GET speed-test 页 -> 解析线路数组 & token
  4) 构造“线路1”登录地址
  5) GET 登录页
  6) 加密账号密码，POST /Member/DoLogin
  7) 打印服务器响应
"""

import re
import sys
import time
import json
import binascii
from pathlib import Path
from dataclasses import dataclass
from typing import Optional, Tuple, List
from urllib.parse import urljoin, urlparse, parse_qs, quote
import logging

import requests
from bs4 import BeautifulSoup  # pip install beautifulsoup4
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5

# 配置文件路径
CONFIG_FILE = "config.json"

# Local fallbacks
LOCAL_PAGE1 = "/mnt/data/page1.html"
LOCAL_LINETEST = "/mnt/data/LineNetTest.js"
LOCAL_LOGINPAGE = "/mnt/data/loginpage.html"

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")

@dataclass
class LineLink:
    index: int
    url: str   # full login URL
    base: str  # base domain

def safe_get(url: str, **kwargs) -> Optional[str]:
    try:
        resp = requests.get(url, timeout=kwargs.get("timeout", 8))
        resp.raise_for_status()
        resp.encoding = resp.apparent_encoding or "utf-8"
        return resp.text
    except Exception as e:
        logging.warning("GET failed for %s (%s); using local fallback if available.", url, e)
        return None

def parse_member_href(html: str, base_url: str) -> Optional[str]:
    soup = BeautifulSoup(html, "html.parser")
    a = soup.find("a", id="btnMember")
    if not a or not a.has_attr("href"):
        return None
    return urljoin(base_url, a["href"])

def load_speed_page(member_href: str) -> str:
    page1_html = safe_get(member_href) or Path(LOCAL_PAGE1).read_text(encoding="utf-8")
    return page1_html

def extract_lines_and_token(page1_html: str) -> Tuple[List[str], Optional[str]]:
    arr_match = re.search(r'arrLine\s*=\s*\[([^\]]+)\]', page1_html)
    lines = []
    if arr_match:
        blob = arr_match.group(1)
        lines = re.findall(r'"([^"]+)"', blob)
    tok_match = re.search(r"token=([0-9A-F]+)", page1_html, re.IGNORECASE)
    token = tok_match.group(1) if tok_match else None
    return lines, token

def build_line_login_url(base: str, token: str) -> str:
    ts = int(time.time() * 1000)
    return f"{base}/Member/Login?_={ts}&token={token}"

def simulate_click_line1(page1_html: str) -> LineLink:
    lines, token = extract_lines_and_token(page1_html)
    if not lines:
        raise RuntimeError("Failed to parse arrLine from page1.html")
    if not token:
        raise RuntimeError("Failed to parse token from page1.html")
    base = lines[0]
    full_url = build_line_login_url(base, token)
    return LineLink(index=1, url=full_url, base=base)

def load_login_page(line_login: LineLink) -> str:
    html = safe_get(line_login.url)
    if html:
        return html
    return Path(LOCAL_LOGINPAGE).read_text(encoding="utf-8")

def parse_login_form(login_html: str) -> str:
    return "/Member/DoLogin"

# RSA parameters from RSAUtil.js
EXPONENT_HEX = "010001"
MODULUS_HEX = ("BECA095A9D6509E1E78AA35D7198D95DF8B308CDA5E0D202E27452D56C312A5D"
               "B35CD62E159CD1A5CDD614316495F514947AD64AE7D0FD357958E7A66DBAA4DBA"
               "A005AF246C07E992FB4C988E5751328B6D2359CB99C38CDEC45AEBD36D9210F35"
               "17C577ECDA31A48F36D46F8A872C55623DFC2C905988D6BF84BCD0D0D7A33B")

def rsa_encrypt_real(text: str) -> str:
    utf8_text = quote(text, safe="~()*!.'")
    n = int(MODULUS_HEX, 16)
    e = int(EXPONENT_HEX, 16)
    rsa_key = RSA.construct((n, e))
    cipher = PKCS1_v1_5.new(rsa_key)
    encrypted_bytes = cipher.encrypt(utf8_text.encode("utf-8"))
    return binascii.hexlify(encrypted_bytes).decode().upper()

def simulate_login(line_login: LineLink, login_html: str, username: str, password: str, captcha: str = ""):
    qs = parse_qs(urlparse(line_login.url).query)
    token = (qs.get("token") or [""])[0]

    endpoint = parse_login_form(login_html)
    post_url = urljoin(line_login.base, endpoint)

    payload = {
        "Account": rsa_encrypt_real(username),
        "Password": rsa_encrypt_real(password),
        "Token": token,
        "Captcha": captcha,
    }

    print("\n--- Sending Login Request ---")
    print("POST", post_url)
    print("Form data:", json.dumps(payload, ensure_ascii=False, indent=2))

    resp = requests.post(post_url, data=payload, timeout=8)
    print("Server status:", resp.status_code)
    print("Response text (first 500 chars):")
    print(resp.text[:500])

def main():
    config = json.loads(Path(CONFIG_FILE).read_text(encoding="utf-8"))
    base_url = config["base_url"]
    safecode = config["safecode"]
    username = config["account"]
    password = config["password"]

    landing_url = f"{base_url}?SafeCode={safecode}"

    print("Step 1) Load landing page and find '会员登入' link ...")
    landing_html = safe_get(landing_url)
    print(landing_html)
    if not landing_html:
        landing_html = """
        <html><body><a id="btnMember" href="/Navigation/Speed?token=FAKE&isbackend=0&ismobile=0">会员登入</a></body></html>
        """
    member_url = parse_member_href(landing_html, landing_url)
    if not member_url:
        raise SystemExit("Could not find the 会员登入 link.")

    print("  Member URL:", member_url)

    print("Step 2) Load speed-test page and simulate '线路1' link ...")
    page1_html = load_speed_page(member_url)
    line1 = simulate_click_line1(page1_html)
    print("  线路1 login URL:", line1.url)

    print("Step 3) Load login page and send POST ...")
    login_html = load_login_page(line1)

    simulate_login(line1, login_html, username=username, password=password, captcha="")

    print("\nDone.")

if __name__ == "__main__":
    main()
